/*

  xmunipack - icon


  Copyright © 2009-2011 F.Hroch (hroch@physics.muni.cz)

  This file is part of Munipack.

  Munipack is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  
  Munipack is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with Munipack.  If not, see <http://www.gnu.org/licenses/>.


  
  IMPORTANT

  ***  All functions MUST BE called in main thread ! ***

  .. else the execution is locked by wxImage::ConvertToImage();


*/


#include "xmunipack.h"
#include <cstring>
#include <wx/wx.h>
#include <wx/graphics.h>



MuniIcon::MuniIcon(const FitsFile& f, const MuniConfig *c):
  fits(f),config(c)
{
  // setup Color
  FitsColor color;
  color.SetTrans("XYZ",config->cdatafile);
 
  // setup Itt
  FitsItt itt(ITT_LINE);

  // create icons for all HDUs
  for(size_t k = 0; k < fits.HduCount(); k++) {

    switch(fits.Hdu(k).Type()) {
    case HDU_IMAGE:
      list.push_back(ImageIcon(FitsArray(fits.Hdu(k)),itt,color));
      break;

    case HDU_HEAD: 
      list.push_back(SymbolIcon(config->head_icon,config->icon_size,
      			      config->icon_size));
      break;

    case HDU_TABLE: 
      list.push_back(SymbolIcon(config->table_icon,config->icon_size,
			       config->icon_size));
      break;
      
    default:
      list.push_back(config->default_icon);
      break;
    }
  }

  // cover icon
  switch(fits.Type()) {
  case FITS_GRAY:
  case FITS_COLOR:
    wxASSERT(fits.HasImage() && /*fits.HduCount() == 1 &&*/
    	     fits.Hdu(0).Type() == HDU_IMAGE && fits.Hdu(0).IsOk());
    icon = list[0];
    break;

  case FITS_MULTI:
    icon = MultiIcon(list);
    break;

  default:
    icon = config->default_icon;
    break;
  }
}


MuniIcon::~MuniIcon()
{
  list.clear();
}

wxImage MuniIcon::ImageIcon(const FitsArray& a, const FitsItt& itt,
			    const FitsColor& color) const
{
  wxASSERT(a.IsOk());

  if( a.Flavour() == HDU_IMAGE_COLOR )
    return ColorIcon(a,itt,color);
  else if ( a.Flavour() == HDU_IMAGE_FRAME )
    return GrayIcon(a,itt);
  else {
    wxFAIL_MSG("----- WARNING: Unreachable code.");
    return wxImage();
  }
}

wxImage MuniIcon::GrayIcon(const FitsArray& a, const FitsItt& itt0) const
{
  FitsArrayStat array(a);
  FitsItt itt(itt0);
  itt.Init(array.Med(),array.Mad());
  FitsImage image(a);
  FitsDisplay display(config->display_colorspace);
  display.SetItt(itt);
  FitsBitmap b = display.GetImage(image.Thumb(config->icon_size,config->icon_size));
  return wxImage(b.GetWidth(),b.GetHeight(),b.NewTopsyTurvyRGB());
}

wxImage MuniIcon::ColorIcon(const FitsArray& a, const FitsItt& itt0,
			    const FitsColor& color0) const
{
  FitsArrayStat array(a.Plane(1));
  FitsItt itt(itt0);
  itt.Init(array.Med(),array.Mad());
  itt.SetKind(ITT_KIND_ABS);
  itt.SetBlack(0.0);
  itt.SetContrast(6.5e4);

  FitsColor color(color0);
  color.SetTrans(a.GetKey("CSPACE"),config->cdatafile);
  for(int i = 0; i < a.Naxes(2); i++) {
    FitsArrayStat s(a.Plane(i));
    color.SetLevel(i,s.Med());
  }

  FitsImage image(a);
  FitsDisplay display(config->display_colorspace);
  display.SetItt(itt);
  display.SetColor(color);
  FitsBitmap b = display.GetImage(image.Thumb(config->icon_size,config->icon_size));
  return wxImage(b.GetWidth(),b.GetHeight(),b.NewTopsyTurvyRGB());
}

wxImage MuniIcon::ColorIcon(const FitsFile& fits, const FitsItt& itt,
			    const FitsColor& color) const
{
  FitsImage image(fits,-1);
  FitsDisplay display(config->display_colorspace);
  display.SetItt(itt);
  display.SetColor(color);
  FitsBitmap b = display.GetImage(image.Thumb(config->icon_size,config->icon_size));
  return wxImage(b.GetWidth(),b.GetHeight(),b.NewTopsyTurvyRGB());
}

wxImage MuniIcon::MultiIcon(const std::vector<wxImage>& list) const
{
  wxBitmap canvas(config->icon_size,config->icon_size);
  wxMemoryDC mdc(canvas);
  if( mdc.IsOk() ) {

    mdc.SetBackground(*wxTRANSPARENT_BRUSH);
    mdc.SetPen(*wxLIGHT_GREY_PEN);

    for(size_t l = 0; l < list.size(); l++) {
      wxBitmap b(list[l]);
      int x = 10*l;
      int y = 10*l;
      mdc.DrawRectangle(x,y,b.GetWidth(),b.GetHeight());
      mdc.DrawBitmap(b,x+1,y+1);
    }
    
    wxString t;
    t.Printf("%d",int(list.size()));
    mdc.SetFont(*wxSMALL_FONT);
    wxSize s = mdc.GetTextExtent(t);
    int w = s.GetWidth() > s.GetHeight() ? s.GetWidth() : s.GetHeight() + 5;
    int x = config->icon_size - w - 1;

    mdc.SetBackground(*wxLIGHT_GREY_BRUSH);
    mdc.DrawRoundedRectangle(x,1,w,w,5.0);
    mdc.DrawText(t,x+w/2-s.GetWidth()/2,3);
  }
  return canvas.ConvertToImage();;

}

wxImage MuniIcon::GetIcon() const
{
  if( icon.IsOk() )
    return icon;
  else
    return config->default_icon;
}


std::vector<wxImage> MuniIcon::GetList() const
{
  return list;
}


wxImage MuniIcon::BrowserIcon(const wxImage& icon,
			      const int cwidth, const int cheight,
			      const wxString& text, const wxColour& bcolour)
{
  wxASSERT(icon.IsOk());
  if( icon.IsOk() ) {

    int width = icon.GetWidth();
    int height = icon.GetHeight();
    int xoff = (cwidth - width)/2;
    int yoff = (cheight - height)/2;
    int dx = 3;
    int dy = 3;

    wxImage temp(cwidth,cheight);

    wxBitmap bitmap(temp);
    wxMemoryDC dc(bitmap);
    wxGraphicsContext *gc = wxGraphicsContext::Create(dc);
    if( gc ) {

      // clear canvas
      gc->SetBrush(bcolour);
      gc->DrawRectangle(0,0,cwidth,cheight);

      // draw black rectangle
      gc->SetBrush(*wxBLACK);
      gc->DrawRectangle(xoff+dx,yoff+dy,width-2*dx,height-2*dy);
      
      delete gc;
    }
    dc.SelectObjectAsSource(wxNullBitmap);
    wxImage blur = bitmap.ConvertToImage();
    wxImage iblur(blur.Blur(2));

    wxBitmap bmp(iblur);
    wxMemoryDC dcc(bmp);
    wxGraphicsContext *gcc = wxGraphicsContext::Create(dcc);
    if( gcc ) {
      gcc->SetPen(*wxWHITE);
      gcc->SetBrush(*wxTRANSPARENT_BRUSH);
      gcc->DrawRectangle(xoff+dx-1,yoff+dy-1,width-2*dx,height-2*dy);
      wxImage img(icon.GetSubImage(wxRect(dx,dx,width-2*dx,height-2*dy)));
      gcc->DrawBitmap(wxBitmap(img),xoff+dx-1,yoff+dy-1,width-2*dx,height-2*dy);

      if( ! text.IsEmpty() ) {
	wxFont fn(*wxSMALL_FONT);
	gcc->SetFont(fn,*wxBLACK);
	double wt,ht,tt;
	gcc->GetTextExtent(text,&wt,&ht,&tt,&tt);
	gcc->SetPen(*wxTRANSPARENT_PEN);
	gcc->SetBrush(bcolour);
	gcc->DrawRoundedRectangle((cwidth-wt)/2-1,cheight-ht-1-dy,wt+4,ht+1,2);
	gcc->DrawText(text,(cwidth-wt)/2,cheight-ht-1-dy);
      }

      delete gcc;
    }
    dcc.SelectObjectAsSource(wxNullBitmap);

    return bmp.ConvertToImage();

  }  
  else {

    int width = 22;
    int height = 22;
    int xoff = cwidth/2 - width/2;
    int yoff = cheight - height;
    int r2 = (height+width)/4;

    int npix = cwidth*cheight;
    unsigned char *rgb = (unsigned char *) malloc(3*npix);
    unsigned char *alpha = (unsigned char *) malloc(npix);

    // fill transparency
    for(int i = 0; i < npix; i++) alpha[i] = 0;

    // draw backround as circle
    for(int j = 0; j < height; j++)
      for(int i = 0; i < width; i++) {
	int x = xoff + i;
	int y = yoff + j;
	int n = x + y*cwidth;
	wxASSERT(n < npix);
	int i2 = i - height/2;
	int j2 = j - width/2;
	if( i2*i2 + j2*j2 < r2*r2 ) {
	  for(int k = 0; k < 3; k++) rgb[3*n+k] = 255;
	  alpha[n] = 255;
	} 
      }

    wxImage temp(cwidth,cheight,rgb);
    temp.SetAlpha(alpha);

    wxBitmap icon(temp);
    wxMemoryDC dc(icon);
    wxGraphicsContext *gc = wxGraphicsContext::Create(dc);
    if( gc ) {
      // draw clock symbol
      wxColour colour(128,128,128);
      gc->SetBrush(gc->CreateBrush(wxBrush(*wxTRANSPARENT_BRUSH)));
      gc->SetPen(gc->CreatePen(wxPen(colour,2,wxSOLID)));
      gc->DrawEllipse(xoff+2,yoff+2,width-4,height-4);
      gc->SetPen(gc->CreatePen(wxPen(colour,1,wxSOLID)));
      delete gc;
    }
    
    dc.SelectObjectAsSource(wxNullBitmap);

    return icon.ConvertToImage();
  }

  // Important: the function should be invoked from
  // main thread only. The wxBitmap.ConvertToImage() calls under GTK+
  // function assigned directy to a window system (to a GC).

}

wxImage MuniIcon::DefaultIcon(int cwidth, int cheight) 
{
  int width = cwidth;
  int height = int(width/1.618);

  int xcen = width/2;
  int ycen = height/2;

  // dimensions of symbol
  wxFont fn(*wxNORMAL_FONT);
  int ssize = fn.GetPointSize()+3;
  int wsym = 3*ssize/2;
  int hsym = wsym;

  wxColour hi(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
  wxColour bg(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
  bg = *wxLIGHT_GREY;
  unsigned char alpha(255);


  wxImage temp(width,height,false);
  
  wxBitmap icon(temp);
  wxMemoryDC dc(icon);
  dc.SetBrush(bg);
  dc.Clear();

  wxGraphicsContext *gc = wxGraphicsContext::Create(dc);
  if( gc ) {

    wxColour c(hi.Red(),hi.Green(),hi.Blue(),alpha);
    wxBrush b(wxColour(bg));

    gc->SetFont(*wxSMALL_FONT,bg);

    gc->SetBrush(c);
    gc->SetPen(wxPen(bg));

    gc->DrawRectangle(0,0,width,height);

    // text
    gc->DrawText(".. default ..",10,10);
    
    // pentagram
    double dx = wsym/2;
    double dy = hsym/2;
    double x1 = xcen - dx;
    double y1 = ycen - dy;
    gc->SetBrush(wxBrush(c,wxTRANSPARENT));
    gc->DrawEllipse(x1,y1,wsym,hsym);
    wxGraphicsPath gp(gc->CreatePath());
    gp.MoveToPoint(xcen,ycen+dy);
    gp.AddLineToPoint(xcen-dx/2,ycen+dy/2);
    gp.AddLineToPoint(xcen+dx,ycen+dy/2);
    gp.AddLineToPoint(xcen+dx,ycen);
    gc->StrokePath(gp);

    delete gc;
  }
  
  dc.SelectObjectAsSource(wxNullBitmap);

  return icon.ConvertToImage();
}


wxImage MuniIcon::SymbolIcon(const wxImage& symbol, int width, int height)
{
  wxASSERT( width > 0 && height > 0);
  wxBitmap canvas(width,height);
  wxMemoryDC image(canvas);
  if( image.IsOk() ) {
    image.SetBrush(wxBrush(*wxWHITE_BRUSH));
    image.DrawRectangle(0,0,width,height);
    image.DrawBitmap(wxBitmap(symbol.Scale(width-20,height-10)),10,5);
  }
  return canvas.ConvertToImage();
}

wxImage MuniIcon::BulletIcon(const wxSize& size, const wxColour& c)
{
  int x0 = size.GetWidth()/2;
  int y0 = size.GetHeight()/2;
  int radius = 20;

  wxColour colour(c.Red(),c.Green(),c.Blue(),255);
  int npix = size.GetWidth()*size.GetHeight();
  unsigned char *rgb = (unsigned char *) malloc(3*npix);

  wxColour cb = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
  wxImage image(size.GetWidth(),size.GetHeight(),rgb);
  for(int i = 0; i < image.GetWidth(); i++)
    for(int j = 0; j < image.GetHeight(); j++) 
      image.SetRGB(i,j,cb.Red(),cb.Green(),cb.Blue());

  wxBitmap bitmap(image);
  wxMemoryDC dc(bitmap);
  wxGraphicsContext *gc = wxGraphicsContext::Create(dc);
  if( gc ) {
    gc->SetPen(wxPen(colour));
    gc->SetBrush(wxBrush(colour));
    wxGraphicsBrush b = 
      gc->CreateRadialGradientBrush(x0-radius/8,y0-radius/8,x0+radius/8,y0+radius/8,
				    3*radius/5,*wxWHITE,colour);
    gc->SetBrush(b);
    gc->DrawEllipse(x0-radius/2,y0-radius/2,radius,radius);
    delete gc;
  }
  dc.SelectObjectAsSource(wxNullBitmap);
  return bitmap.ConvertToImage();
}


wxImage MuniIcon::ListIcon(const wxImage& icon, int size, const wxColour& c)
{
  wxASSERT(icon.IsOk() && icon.GetHeight() > 0);
  wxSize s;
  wxPoint p;
  double r = double(icon.GetWidth()) / double(icon.GetHeight());
  if( icon.GetWidth() > icon.GetHeight() ) {
    float x = r > 0.0 ? size/r : 1;
    if( x < 1 ) x = 1;
    s = wxSize(size,int(x));
    p = wxPoint(0,(size-s.GetHeight())/2);
  }
  else {
    float x = size*r;
    if( x < 1 ) x = 1;
    s = wxSize(int(x),size);
    p = wxPoint((size-s.GetWidth())/2,0);
  }
  wxImage img = icon.Scale(s.GetWidth(),s.GetHeight(),wxIMAGE_QUALITY_HIGH);

  for(int i = 0; i < img.GetWidth(); i++)
    for(int j = 0; j < img.GetHeight(); j++) {
      unsigned char r = (c.Red()*img.GetRed(i,j))/255;
      unsigned char g = (c.Green()*img.GetGreen(i,j))/255;
      unsigned char b = (c.Blue()*img.GetBlue(i,j))/255;
      img.SetRGB(i,j,r,g,b);
    }
  
  return img.Size(wxSize(size,size),p);
}
